package mapred.util;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public abstract class Configuration<K, V> implements Configurable<K, V> {

    private Map<K, V> map;

    private String kvDelim;

    private String entryDelim;

    public Configuration(String s, String kvDelim, String entryDelim) {
        map = new HashMap<K, V>();
        this.kvDelim = kvDelim;
        this.entryDelim = entryDelim;
        decode(s);
    }

    private void decode(String s) {
        if (s != null) {
            for (String t : s.split(entryDelim)) {
                this.add(t);
            }
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (K k : this) {
            sb.append(entryDelim);
            sb.append(k.toString());
            sb.append(kvDelim);
            sb.append(this.get(k).toString());
        }
        if (sb.length() > 0) {
            return sb.substring(entryDelim.length());
        } else {
            return "";
        }
    }

    public void add(Configuration<K, V>... others) {
        for (Configuration<K, V> o : others) {
            for (K k : o) {
                this.add(k, o.get(k));
            }
        }
    }

    public void add(String t) {
        if (!t.isEmpty()) {
            int pos = t.indexOf(kvDelim);
            K key = decodeKey(t.substring(0, pos));
            V value = decodeValue(t.substring(pos + kvDelim.length()));
            this.add(key, value);
        }
    }

    public void add(K key, V value) {
        if (isValidKey(key)) {
            if (this.contains(key)) {
                this.set(key, mergeValue(key, map.get(key), value));
            } else {
                this.set(key, value);
            }
        }
    }

    @Override
    public boolean contains(K key) {
        return map.containsKey(normalizeKey(key));
    }

    @Override
    public V get(K key) {
        return map.get(normalizeKey(key));
    }

    @Override
    public void remove(K key) {
        map.remove(normalizeKey(key));
    }

    @Override
    public void set(K key, V value) {
        map.put(normalizeKey(key), value);
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public Iterator<K> iterator() {
        return map.keySet().iterator();
    }

    protected K normalizeKey(K key) {
        return key;
    }
    
    protected abstract K decodeKey(String strKey);

    protected abstract V decodeValue(String strValue);

    protected abstract boolean isValidKey(K key);
    
    protected abstract V mergeValue(K key, V oldV, V newV);

}
